package com.summli.basic.class06;


import java.util.HashMap;

/**
 * 一种特殊的单链表节点类描述如下
 * class Node {
 * int value;
 * Node next;
 * Node rand;
 * Node(int val) { value = val; }
 * }
 * rand指针是单链表节点结构中新增的指针，rand可能指向链表中的任意一个节点，也可能指向null。
 * 给定一个由Node节点类型组成的无环单链表的头节点 head，请实现一个函数完成这个链表的复制，并返回复制的新链表的头节点。
 * 【要求】
 * 时间复杂度O(N)，额外空间复杂度O(1)
 *
 * 单向链表如何实现深度COPY
 */
public class Code04_CopyListWithRandom {

    public static class Node {
        public int value;
        public Node next;
        public Node rand;

        public Node(int data) {
            this.value = data;
        }
    }
    // 方法1：使用hashMap存储old Node 和CopyNode 的对应关系，这样，就可以通过old Node查找到CopyNode，
    // 就可以链接copy之后的Node节点
    public static Node copyListWithRand1(Node head) {
        if(head == null){
            return null;
        }
        HashMap<Node,Node> map = new HashMap<>();
        Node cur = head;
        while(cur != null){
            Node copyNode = new Node(cur.value);
            map.put(cur, copyNode);
            cur = cur.next;
        }
        cur = head;
        while(cur != null){
            map.get(cur).next = map.get(cur.next);
            map.get(cur).rand = map.get(cur.rand);
            cur = cur.next;
        }
        return map.get(head);
    }

    // 方法2：题目要求额外的空间复杂度位O（1）
    // 最主要的方式是：可以通过老的链表来找到我们copy的链表
    public static Node copyListWithRand2(Node head) {
        if(head == null){
            return null;
        }
        Node cur = head;
        while(cur != null){
            Node copyNode = new Node(cur.value);
            Node next = cur.next;
            cur.next = copyNode;
            copyNode.next = next;
            cur = next;
        }
        cur = head;
        while(cur != null){
            Node next = cur.next.next;
            cur.next.rand = cur.rand == null ? null:cur.rand.next;
            cur = next;
        }
        // 分离两个链表
        cur = head;
        Node copy  = head.next;
        while(cur != null){
            Node next = cur.next;
            cur.next = next == null? null: next.next;
            cur = next;
        }
        return copy;
    }

    // for test
    public static void printRandLinkedList(Node head) {
        Node cur = head;
        System.out.print("order: ");
        while (cur != null) {
            System.out.print(cur.value + " ");
            cur = cur.next;
        }
        System.out.println();
        cur = head;
        System.out.print("rand:  ");
        while (cur != null) {
            System.out.print(cur.rand == null ? "- " : cur.rand.value + " ");
            cur = cur.next;
        }
        System.out.println();
    }

    public static void main(String[] args) {
        Node head = null;
        Node res1 = null;
        Node res2 = null;
        System.out.println("=========================");

        head = new Node(1);
        head.next = new Node(2);
        head.next.next = new Node(3);
        head.next.next.next = new Node(4);
        head.next.next.next.next = new Node(5);
        head.next.next.next.next.next = new Node(6);

        head.rand = head.next.next.next.next.next; // 1 -> 6
        head.next.rand = head.next.next.next.next.next; // 2 -> 6
        head.next.next.rand = head.next.next.next.next; // 3 -> 5
        head.next.next.next.rand = head.next.next; // 4 -> 3
        head.next.next.next.next.rand = null; // 5 -> null
        head.next.next.next.next.next.rand = head.next.next.next; // 6 -> 4
        System.out.println("============datameta=============");
        printRandLinkedList(head);
        System.out.println("============ONE=============");
        res1 = copyListWithRand1(head);
        printRandLinkedList(res1);
        System.out.println("============head:");
        printRandLinkedList(head);
        System.out.println("=============TWO============");
        res2 = copyListWithRand2(head);
        printRandLinkedList(res2);
        System.out.println("============head:");
        printRandLinkedList(head);


    }
}
